--------------------------------------------------
-- @file src/kit/core/checker.lua
-- @desc a checker for data
-- @author doooreyn
-- @date 2020.11.14
-- @depends tolua
-- @more 数据检查器
--------------------------------------------------
local checker = {
    -- lua基础类型
    types = {
        void = "nil",
        boolean = "boolean",
        number = "number",
        table = "table",
        string = "string",
        userdata = "userdata",
        thread = "thread",
        func = "function"
    }
}
local tolua = tolua

--
-- @method checker.is_null
-- @desc 判断数据是否为null
-- @param v any 数据
-- @return boolean
--
function checker.is_null(v)
    return tolua.is_null(v)
end

--
-- @method checker.is_nil
-- @desc 判断数据是否为nil
-- @param v any 数据
-- @return boolean
--
function checker.is_nil(v)
    return type(v) == checker.types.void
end

--
-- @method checker.is_bool
-- @desc 判断数据是否为boolean
-- @param v any 数据
-- @return boolean
--
function checker.is_bool(v)
    return type(v) == checker.types.boolean
end

--
-- @method checker.is_number
-- @desc 判断数据是否为数值
-- @param v any 数据
-- @return boolean
--
function checker.is_number(v)
    return type(v) == checker.types.number
end

--
-- @method checker.is_table
-- @desc 判断数据是否为table
-- @param v any 数据
-- @return boolean
--
function checker.is_table(v)
    return type(v) == checker.types.table
end

--
-- @method checker.is_string
-- @desc 判断数据是否为string
-- @param v any 数据
-- @return boolean
--
function checker.is_string(v)
    return type(v) == checker.types.string
end

--
-- @method checker.is_userdata
-- @desc 判断数据是否为userdata
-- @param v any 数据
-- @return boolean
--
function checker.is_userdata(v)
    return type(v) == checker.types.userdata
end

--
-- @method checker.is_thread
-- @desc 判断数据是否为thread
-- @param v any 数据
-- @return boolean
--
function checker.is_thread(v)
    return type(v) == checker.types.thread
end

--
-- @method checker.is_function
-- @desc 判断数据是否为function
-- @param v any 数据
-- @return boolean
function checker.is_function(v)
    return type(v) == checker.types.func
end

--
-- @method checker.is_class
-- @desc 判断数据是否为类
-- @param v any 数据
-- @return boolean
--
function checker.is_class(v)
    return checker.is_table(v) and (v[".isclass"] or v.isclass == 0x1)
end

--
-- @method checker.type
-- @desc 获得数据类型
-- @param v any 数据
-- @return string
--
function checker.type(v)
    return tolua.type(v)
end

--
-- @method checker.is_type_of
-- @desc 判断数据是否符合目标类型
-- @param v any 数据
-- @param t string 期望的数据类型
-- @return boolean
--
function checker.is_type_of(v, t)
    return checker.type(v) == t
end

--
-- @method checker.exist_key
-- @desc 检查数据是否存在字段
-- @param v any 数据
-- @param v any 键名
-- @return boolean
--
function checker.exist_key(v, k)
    return (checker.is_table(v) or checker.is_userdata(v)) and checker.is_nil(v[k])
end

--
-- @method checker.is_kind_of
-- @desc 检查对象类型是否匹配
-- @param v table(class) 类
-- @param v classname string 类名
-- @return boolean
--
function checker.is_kind_of(obj, classname)
    local t = type(obj)
    local mt
    if t == "table" then
        mt = getmetatable(obj)
    elseif t == "userdata" then
        mt = tolua.getpeer(obj)
    end
    while mt do
        if mt.__cname == classname then
            return true
        end
        mt = mt.super
    end

    return false
end

--
-- @method checker.some
-- @desc 对参数列表进行或运算，检测数据是否参数中的一项
-- @param v 数据
-- @param ... any 参数列表
-- @return boolean
--
function checker.some(v, ...)
    local result = false
    for _, p in ipairs({...}) do
        if v == p then
            result = true
            break
        end
    end
    return result
end

--
-- @method checker.all
-- @desc 对参数列表进行与运算，检测数据是否与所有参数匹配
-- @param v 数据
-- @param ... any 参数列表
-- @return boolean
--
function checker.all(v, ...)
    local result = true
    for _, p in ipairs({...}) do
        if v ~= p then
            result = false
            break
        end
    end
    return result
end

--
-- @method checker.none
-- @desc 对参数列表进行非运算，检测数据是否不在参数中
-- @param v 数据
-- @param ... any 参数列表
-- @return boolean
--
function checker.none(v, ...)
    return not checker.some(v, ...)
end

--
-- @method checker.expect
-- @desc 断言数据是否与期望值一致
-- @param v 数据
-- @param t 期望的数据
-- @param name 断言的名称
--
function checker.expect(v, t, name)
    name = name or ""
    local fmt = "%s expect %s, but got %s"
    assert(v == t, fmt:format(name, tostring(t), tostring(v)))
end

--
-- @method checker.expect
-- @desc 断言数据是否与期望值一致
-- @param v 数据
-- @param t 期望的数据
-- @param name 断言的名称
--
function checker.expect_not(v, t, name)
    name = name or ""
    local fmt = "%s expect %s, but got %s"
    assert(not (v == t), fmt:format(name, tostring(t), tostring(v)))
end

--
-- @method checker.expect_type
-- @desc 检查数据类型是否与期望值一致
-- @param v 数据
-- @param t 期望的数据类型
-- @param name 断言的名称
--
function checker.expect_type(v, t, name)
    checker.expect(type(v), t, name)
end

return checker
